#include "stdafx.h"
#include "HeightField.h"
#include "Marco.h"

HeightField::~HeightField()
{
	SAFE_DEL_ARRAY(HeightData); //Macro in Constant.h, todo: delete array poiter and it is setted NULL 
}

GLubyte* HeightField::OpenSourceFile(char *fileSrcName, long &size)
{
	FILE *pFile = fopen(fileSrcName, "rb");
	if (!pFile)
	{
		return 0;
	}

	fseek(pFile, 0, SEEK_END);
	size = ftell(pFile); //Size of file is reading
	fseek(pFile, 0, SEEK_SET);

	char *pBuffer = new char[size];

	fread(pBuffer, 1, size, pFile);

	fclose(pFile);
	
	return (GLubyte*)pBuffer;
}

void HeightField::ReadHeightFromColor8Bit(GLfloat *Des, GLubyte *Src)
{
	for (int r = 0; r<Rows; r++)
	{
		for (int c = 0; c < Cols; c++)
		{
			Des[r*Cols + c] = Src[r*Cols + c];
		}
	}
}

void HeightField::ReadHeightFromColor16Bit(GLfloat *Des, GLushort *Src)
{
	GLushort temp; //Variable store value of Src when read each element
	GLubyte red, green, blue;
	for (int r = 0; r<Rows; r++)
	{
		for (int c = 0; c < Cols; c++)
		{
			temp = Src[r*Cols + c];
			red = (temp & 0xF800)>>11;
			green = (temp & 0x7E0)>>5;
			blue = temp & 0x1F;
			Des[r*Cols + c] = (GLfloat)((red + green + blue) / 3); //value from 0 to 255
		}
	}
}

bool HeightField::IsValid(int row, int col)
{
	if (row >= 0 && row < Rows &&
		col >= 0 && col < Cols)
		return true;
	return false;
}

GLfloat HeightField::AveragerHeight3x3(int row, int col)
{
	float sum = 0.0f; //Store height value of about 9 vertex around it 
	float num = 0.0f; //Count of vertex are calculated

	for (int r = row - 1; r <= row + 1; r ++ )
	{
		for (int c = col - 1; c <= col + 1; c++)
		{
			if (IsValid(r, c))
			{
				sum += HeightData[r*Cols + c];
				num++;
			}
		}
	}
	return (GLfloat)sum / num;
}

void HeightField::Filter3x3()
{
	GLfloat *pBuffer = new GLfloat[Rows * Cols];
	for (int r = 0; r < Rows; r++)
	{
		for (int c = 0; c < Cols; c++)
		{
			pBuffer[r*Cols + c] = AveragerHeight3x3(r, c);
		}
	}
	SAFE_DEL_ARRAY(HeightData);

	HeightData = new GLfloat[Rows * Cols];
	memcpy(HeightData, pBuffer, Rows*Cols*sizeof(GLfloat));
	SAFE_DEL_ARRAY(pBuffer);
}

int HeightField::LoadHeightData(char *fileSrcName, int iRows, int iCols, float heightScale)
{
	Rows = iRows;
	Cols = iCols;

	long fileSize;
	GLubyte *pBufferBaseSrc; //Data base not config

	//Load data source
	pBufferBaseSrc = OpenSourceFile(fileSrcName, fileSize);

	if (!pBufferBaseSrc)
	{
		return -1;
	}

	int colorType = fileSize / (iRows * iCols); //Type of height map color (this function only read color type 8/16)
	
	if (colorType != 1 && colorType != 2)
	{
		return -3; //Color type isn't support for this function
	}

	HeightData = new GLfloat[iRows * iCols];

	switch (colorType)
	{
	case 1: //In color 8-Bit heightfield are stored in 1-Byte
		ReadHeightFromColor8Bit(HeightData, pBufferBaseSrc);
		break;
	case 2: //In color 16-Bit heightfield are stored in 2-Byte
		ReadHeightFromColor16Bit(HeightData, (GLushort*)pBufferBaseSrc);
		break;
	};

	//Set height scale for all vertex
	for (int i = 0; i < fileSize / colorType; i++)
	{
		HeightData[i] *= heightScale;
	}

	//set averaga height value of 9 vertex around for vertex center;
	Filter3x3();

	SAFE_DEL_ARRAY(pBufferBaseSrc);
	return 0;
}

float HeightField::getHeight(int row, int col)
{
	if (IsValid(row, col))
		return HeightData[row*Cols + col];
	return -1;
}